package com.etone.smartAudit.annotation.support;

import com.etone.smartAudit.annotation.AdminOperation;
import com.etone.smartAudit.domain.*;
import com.etone.smartAudit.dto.admin.AdminDTO;
import com.etone.smartAudit.dto.auditRisk.AuditRiskDTO;
import com.etone.smartAudit.dto.email.AuditExceptionEmailDTO;
import com.etone.smartAudit.dto.fieldCase.FieldCaseDTO;
import com.etone.smartAudit.dto.iapDataModel.IapDataModelDTO;
import com.etone.smartAudit.dto.iapPlatform.IapPlatformDTO;
import com.etone.smartAudit.dto.item.ItemDTO;
import com.etone.smartAudit.dto.task.BaseTaskDTO;
import com.etone.smartAudit.dto.task.TaskAuditDTO;
import com.etone.smartAudit.dto.task.WorkTaskDTO;
import com.etone.smartAudit.dto.toolCase.ToolCaseDTO;
import com.etone.smartAudit.error.CustomParameterizedException;
import com.etone.smartAudit.mapper.*;
import com.etone.smartAudit.security.SecurityUtils;
import lombok.AllArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.zalando.problem.Status;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Created by 蒋学伟 on 2017/8/30.
 */
@Aspect
@Component
@Service
@AllArgsConstructor
public class AdminOperationAop {

    AdminLogMapper adminLogMapper;

    AdminMapper adminMapper;

    AuditRiskMapper auditRiskMapper;

    TaskMapper taskMapper;

    ToolMapper toolMapper;

    ToolCaseMapper toolCaseMapper;

    AuditExceptionMonthMapper auditExceptionMonthMapper;

    ItemMapper itemMapper;

    IapPlatformMapper iapPlatformMapper;

    IapDataModelMapper iapDataModelMapper;

    private final String add = "添加";
    private final String edit = "编辑";
    private final String delete = "删除";
    private final String batchDelete = "批量删除";

    private final String download = "下载";

    private final String audit = "审核";

    private final String login = "登陆";
    private final String logout = "退出";

    @Pointcut("execution(* com.etone.smartAudit.controller..*(..)) && @annotation(com.etone.smartAudit.annotation.AdminOperation)")
    public void controllerMethodPointcut() {
    }


    @Around("execution(* com.etone.smartAudit.controller..*(..)) && @annotation(com.etone.smartAudit.annotation.AdminOperation)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method targetMethod = signature.getMethod();
        if (targetMethod.isAnnotationPresent(AdminOperation.class)) {
            Object[] classes = joinPoint.getArgs();
            String[] parameterNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();

            AdminOperation adminOperation = targetMethod.getAnnotation(AdminOperation.class);
            String module = adminOperation.module();
            String item = adminOperation.item();
            String serverName = adminOperation.serverName();
            String content = adminOperation.content();

            //before操作
            AdminLog beforeAdminLog = content(module, item, content, classes, parameterNames, true);
            content = beforeAdminLog.getContent();
            item = beforeAdminLog.getItem();

            Object obj = joinPoint.proceed();

            String username = SecurityUtils.getCurrentUserLogin();
            Admin admin = adminMapper.findByAccount(username);
            if (admin == null) {
                throw new CustomParameterizedException("用户不存在", Status.FORBIDDEN);
            }

            //after操作
            AdminLog afterAdminLog = content(module, item, content, classes, parameterNames, false);
            content = afterAdminLog.getContent();
            item = afterAdminLog.getItem();

            AdminLog adminLog = AdminLog.builder()
                    .adminUsername(username)
                    .adminNickName(admin.getNickName())
                    .adminId(admin.getId())
                    .adminCompanyName(admin.getCompanyName())
                    .item(item)
                    .module(module)
                    .serverName(serverName)
                    .content(content)
                    .createTime(new Date())
                    .build();
            adminLogMapper.insert(adminLog);
            return obj;
        }
        return joinPoint.proceed();
    }

    /**
     * @param module   模块
     * @param item     操作
     * @param content  详情内容
     * @param isBefore 是否是
     * @return
     */
    private AdminLog content(String module, String item, String content, Object[] classes, String[] parameterNames, Boolean isBefore) {
        if ("账号操作".equals(module)) {
            return adminLog(item, content, classes, parameterNames, isBefore);
        } else if ("风险点管控".equals(module)) {
            return auditRiskContent(item, content, classes, parameterNames, isBefore);
        } else if ("任务操作".equals(module)) {
            return taskContent(item, content, classes, parameterNames, isBefore);
        } else if ("工具中台".equals(module)) {
            return toolContent(item, content, classes, parameterNames, isBefore);
        } else if ("持续审计".equals(module)) {
            return auditExceptionContent(item, content, classes, parameterNames, isBefore);
        } else if ("项目管理".equals(module)) {
            return itemContent(item, content, classes, parameterNames, isBefore);
        } else if ("审计案例库".equals(module)) {
            return fieldCaseContent(item, content, classes, parameterNames, isBefore);
        } else if ("系统信息查询".equals(module)) {
            return iapPlatformContent(item, content, classes, parameterNames, isBefore);
        } else if ("元数据查询".equals(module)) {
            return iapDataModelContent(item, content, classes, parameterNames, isBefore);
        }
        return AdminLog.builder().item(item).content(content).build();
    }

    /**
     * 记录管理员操作日志
     *
     * @param item
     * @param content
     * @param classes
     * @param parameterNames
     * @param isBefore
     * @return
     */
    private AdminLog adminLog(String item, String content, Object[] classes, String[] parameterNames, Boolean isBefore) {
        if (isBefore && delete.equals(item)) {
            Integer adminId = getParam(parameterNames, classes, "id", Integer.class);
            Admin deleteAdmin = adminMapper.selectById(adminId);
            if (deleteAdmin != null) {
                content = item + formatContent(deleteAdmin.getNickName());
            }
        } else if (isBefore && batchDelete.equals(item)) {
            List<Integer> adminIds = getParamArray(parameterNames, classes, "ids", Integer.class);
            List<Admin> deleteAdmins = adminMapper.selectBatchIds(adminIds);
            if (deleteAdmins != null) {
                content = batchDelete + formatContent(deleteAdmins.stream().map(deleteAdmin -> deleteAdmin.getNickName()).collect(Collectors.joining(",")));
            }
        } else if (!isBefore && (edit.equals(item) || add.equals(item))) {
            AdminDTO adminDTO = getParam(parameterNames, classes, "adminDTO", AdminDTO.class);
            content = item + formatContent(adminDTO.getNickName());
        }
        return AdminLog.builder().item(item).content(content).build();
    }

    /**
     * 审计风险日志
     *
     * @param item
     * @param content
     * @param classes
     * @param parameterNames
     * @param isBefore
     * @return
     */
    private AdminLog auditRiskContent(String item, String content, Object[] classes, String[] parameterNames, Boolean isBefore) {
        if (!isBefore && (edit.equals(item) || add.equals(item))) {
            AuditRiskDTO auditRiskDTO = getParam(parameterNames, classes, "auditRiskDTO", AuditRiskDTO.class);
            content = item + formatContent(auditRiskDTO.getDoubts());
        } else if (isBefore && delete.equals(item)) {
            Integer auditRiskId = getParam(parameterNames, classes, "id", Integer.class);
            AuditRisk auditRisk = auditRiskMapper.selectById(auditRiskId);
            if (auditRisk != null) {
                content = item + formatContent(auditRisk.getDoubts());
            }
        } else if (isBefore && batchDelete.equals(item)) {
            List<Integer> auditRisks = getParamArray(parameterNames, classes, "ids", Integer.class);
            List<AuditRisk> deleteAuditRisks = auditRiskMapper.selectBatchIds(auditRisks);
            if (deleteAuditRisks != null) {
                content = batchDelete + formatContent(deleteAuditRisks.stream().map(deleteAdmin -> deleteAdmin.getDoubts()).collect(Collectors.joining(",")));
            }
        }
        return AdminLog.builder().item(item).content(content).build();
    }

    /**
     * 任务日志
     *
     * @param item
     * @param content
     * @param classes
     * @param parameterNames
     * @param isBefore
     * @return
     */
    private AdminLog taskContent(String item, String content, Object[] classes, String[] parameterNames, Boolean isBefore) {
        if (!isBefore && ("添加工作任务".equals(item) || "编辑工作任务".equals(item))) {
            item = item.contains(add) ? add : edit;
            WorkTaskDTO workTaskDTO = getParam(parameterNames, classes, "workTaskDTO", WorkTaskDTO.class);
            List<BaseTaskDTO> baseTasks = workTaskDTO.getBaseTasks();
            if (!CollectionUtils.isEmpty(baseTasks)) {
                for (BaseTaskDTO baseTask : baseTasks) {
                    content = item + formatContent(formatItemName(baseTask.getItemName()) + baseTask.getName());
                }

            }
        } else if (!isBefore && (add.equals(item) || edit.equals(item))) {
            List<String> taskDTONames = Arrays.asList("moduleTaskDTO", "temporaryTaskDTO", "dataModelAuthTaskDTO", "resourceTaskDTO", "newDataApplyDTO", "dataMetaTaskForTaskDTO", "resourceDeviceTaskDTO");
            int index = -1;
            for (String taskDTOName : taskDTONames) {
                index = (Arrays.asList(parameterNames).indexOf(taskDTOName));
                if (index > -1) {
                    break;
                }
            }
            BaseTaskDTO baseTaskDTO = (BaseTaskDTO) classes[index];
            content = item + formatContent(formatItemName(baseTaskDTO.getItemName()) + baseTaskDTO.getName());
        } else if (!isBefore && audit.equals(item)) {
            //id
            Integer id = getParam(parameterNames, classes, "id", Integer.class);
            Task task = taskMapper.findById(id);

            //任务对象
            TaskAuditDTO taskAuditDTO = getParam(parameterNames, classes, "taskAuditDTO", TaskAuditDTO.class);
            switch (taskAuditDTO.getState()) {
                case PASS:
                case EXECUTE_NOT_START:
                    item = "审批通过";
                    break;
                case REJECT:
                case EXECUTE_REJECT:
                    item = "审核驳回";
                    break;
                case EXECUTE_STARTING:
                    item = "开始";
                    break;
                case EXECUTE_CHECKING:
                    item = "完结";
                    break;
                default:
                    break;
            }
            content = item + formatContent(formatItemName(task.getItemName()) + task.getName());

        } else if (isBefore && delete.equals(item)) {
            Integer id = getParam(parameterNames, classes, "id", Integer.class);
            Task task = taskMapper.findById(id);
            content = item + formatContent(formatItemName(task.getItemName()) + task.getName());
        }
        return AdminLog.builder().item(item).content(content).build();
    }

    /**
     * 工具中台日志
     *
     * @param item
     * @param content
     * @param classes
     * @param parameterNames
     * @param isBefore
     * @return
     */
    private AdminLog toolContent(String item, String content, Object[] classes, String[] parameterNames, Boolean isBefore) {
        if (!isBefore && "添加案例".equals(item)) {
            item = add;
            Integer toolId = getParam(parameterNames, classes, "id", Integer.class);
            Tool tool = toolMapper.selectById(toolId);
            ToolCaseDTO toolCaseDTO = getParam(parameterNames, classes, "toolCaseDTO", ToolCaseDTO.class);
            content = item + formatContent(formatItemName(tool.getName()) + toolCaseDTO.getName());
        } else if (!isBefore && (download.equals(item) || "在线使用".equals(item))) {
            Integer toolId = getParam(parameterNames, classes, "id", Integer.class);
            Tool tool = toolMapper.selectById(toolId);
            content = item + formatContent(tool.getName());
        } else if ((!isBefore && "下载案例".equals(item)) || (isBefore && "删除案例".equals(item))) {
            item = "下载案例".equals(item) ? download : ("删除案例".equals(item) ? delete : item);

            //工具
            Integer toolId = getParam(parameterNames, classes, "id", Integer.class);
            Tool tool = toolMapper.selectById(toolId);

            //案例
            Integer caseId = getParam(parameterNames, classes, "caseId", Integer.class);
            ToolCase toolCase = toolCaseMapper.selectById(caseId);

            content = item + formatContent(formatItemName(tool.getName()) + toolCase.getName());
        }
        return AdminLog.builder().item(item).content(content).build();
    }

    /**
     * 持续审计
     *
     * @param item
     * @param content
     * @param classes
     * @param parameterNames
     * @param isBefore
     * @return
     */
    private AdminLog auditExceptionContent(String item, String content, Object[] classes, String[] parameterNames, Boolean isBefore) {
        if (!isBefore && "批量下载".equals(item)) {
            List<Integer> ids = getParamArray(parameterNames, classes, "ids", Integer.class);
            List<AuditExceptionMonth> auditExceptionMonths = auditExceptionMonthMapper.selectBatchIds(ids);
            content = auditExceptionMonths.stream().map(auditExceptionMonth -> auditExceptionMonth.getFileName()).collect(Collectors.joining(","));
            content = item + formatContent(content);
        } else if (!isBefore && download.equals(item)) {
            Integer id = getParam(parameterNames, classes, "id", Integer.class);
            AuditExceptionMonth auditExceptionMonth = auditExceptionMonthMapper.selectById(id);
            content = item + formatContent(auditExceptionMonth.getFileName());
        } else if (!isBefore && "发送邮件".equals(item)) {
            AuditExceptionEmailDTO auditExceptionEmailDTO = getParam(parameterNames, classes, "emailDTO", AuditExceptionEmailDTO.class);
            content = "发送" + formatContent(auditExceptionEmailDTO.getSubject() + "到" + auditExceptionEmailDTO.getTo().stream().collect(Collectors.joining(",")));
        }
        return AdminLog.builder().item(item).content(content).build();
    }

    /**
     * 项目任务
     *
     * @param item
     * @param content
     * @param classes
     * @param parameterNames
     * @param isBefore
     * @return
     */
    private AdminLog itemContent(String item, String content, Object[] classes, String[] parameterNames, Boolean isBefore) {
        if (!isBefore && (edit.equals(item) || add.equals(item))) {
            ItemDTO itemDTO = getParam(parameterNames, classes, "itemDTO", ItemDTO.class);
            content = item + formatContent(itemDTO.getName());
        } else if (isBefore && delete.equals(item)) {
            Integer id = getParam(parameterNames, classes, "id", Integer.class);
            Item itemDomain = itemMapper.selectById(id);
            content = item + formatContent(itemDomain.getName());
        } else if (isBefore && batchDelete.equals(item)) {
            List<Integer> ids = getParamArray(parameterNames, classes, "ids", Integer.class);
            List<Item> items = itemMapper.selectBatchIds(ids);
            String itemNames = items.stream().map(itemDomain -> itemDomain.getName()).collect(Collectors.joining(","));
            content = item + formatContent(itemNames);
        } else if (!isBefore && ("资源申请".equals(item) || "资源扩容".equals(item))) {
            Integer id = getParam(parameterNames, classes, "id", Integer.class);
            Item itemDomain = itemMapper.selectById(id);
            content = "申请" + formatContent(formatItemName(itemDomain.getName()) + item);
            item = "申请任务";
        }
        return AdminLog.builder().item(item).content(content).build();
    }

    /**
     * 审计案例库
     *
     * @param item
     * @param content
     * @param classes
     * @param parameterNames
     * @param isBefore
     * @return
     */
    private AdminLog fieldCaseContent(String item, String content, Object[] classes, String[] parameterNames, Boolean isBefore) {
        if (!isBefore && (edit.equals(item) || add.equals(item))) {
            FieldCaseDTO fieldCaseDTO = getParam(parameterNames, classes, "fieldCaseDTO", FieldCaseDTO.class);
            content = item + formatContent(formatItemName(fieldCaseDTO.getFieldName()) + fieldCaseDTO.getName());
        } else if (isBefore && delete.equals(item)) {
            //领域名称
            String fieldName = getParam(parameterNames, classes, "fieldName", String.class);

            //案例名称
            String fieldCaseName = getParam(parameterNames, classes, "fieldCaseName", String.class);

            content = item + formatContent(formatItemName(fieldName) + fieldCaseName);
        } else if (!isBefore && download.equals(item)) {
            //领域名称
            String fieldName = getParam(parameterNames, classes, "fieldName", String.class);

            //案例名称
            String fieldCaseName = getParam(parameterNames, classes, "fieldCaseName", String.class);

            //文件名称
            String fileName = getParam(parameterNames, classes, "fileName", String.class);

            content = item + formatContent(formatItemName(fieldName) + formatItemName(fieldCaseName) + fileName);
        }
        return AdminLog.builder().item(item).content(content).build();
    }

    /**
     * 系统信息平台日志
     *
     * @param item
     * @param content
     * @param classes
     * @param parameterNames
     * @param isBefore
     * @return
     */
    private AdminLog iapPlatformContent(String item, String content, Object[] classes, String[] parameterNames, Boolean isBefore) {
        if (!isBefore && (edit.equals(item) || add.equals(item))) {
            IapPlatformDTO iapPlatformDTO = getParam(parameterNames, classes, "iapPlatformDTO", IapPlatformDTO.class);
            content = item + formatContent(iapPlatformDTO.getName());
        } else if (isBefore && delete.equals(item)) {
            Integer id = getParam(parameterNames, classes, "id", Integer.class);
            IapPlatform iapPlatform = iapPlatformMapper.selectById(id);
            content = item + formatContent(iapPlatform.getName());
        } else if (isBefore && batchDelete.equals(item)) {
            List<Integer> ids = getParamArray(parameterNames, classes, "ids", Integer.class);
            List<IapPlatform> iapPlatforms = iapPlatformMapper.selectBatchIds(ids);
            content = item + formatContent(iapPlatforms.stream().map(iapPlatform -> iapPlatform.getName()).collect(Collectors.joining(",")));
        }
        return AdminLog.builder().item(item).content(content).build();
    }

    /**
     * 元数据查询日志
     *
     * @param item
     * @param content
     * @param classes
     * @param parameterNames
     * @param isBefore
     * @return
     */
    private AdminLog iapDataModelContent(String item, String content, Object[] classes, String[] parameterNames, Boolean isBefore) {
        if (!isBefore && (edit.equals(item) || add.equals(item))) {
            IapDataModelDTO iapDataModelDTO = getParam(parameterNames, classes, "iapDataModelDTO", IapDataModelDTO.class);
            content = item + formatContent(iapDataModelDTO.getCnTableName());
        } else if (isBefore && delete.equals(item)) {
            Integer id = getParam(parameterNames, classes, "id", Integer.class);
            IapDataModel iapPlatform = iapDataModelMapper.selectById(id);
            content = item + formatContent(iapPlatform.getCnTableName());
        } else if (isBefore && batchDelete.equals(item)) {
            List<Integer> ids = getParamArray(parameterNames, classes, "ids", Integer.class);
            List<IapDataModel> iapDataModels = iapDataModelMapper.selectBatchIds(ids);
            content = item + formatContent(iapDataModels.stream().map(iapDataModel -> iapDataModel.getCnTableName()).collect(Collectors.joining(",")));
        }
        return AdminLog.builder().item(item).content(content).build();
    }

    /**
     * 获取参数
     *
     * @param parameterNames
     * @param classes
     * @param paramName
     * @param clazz
     * @param <T>
     * @return
     */
    private <T> T getParam(String[] parameterNames, Object[] classes, String paramName, Class<T> clazz) {
        int index = Arrays.asList(parameterNames).indexOf(paramName);
        T value = (T) classes[index];
        return value;
    }

    /**
     * 获取集合参数
     *
     * @param parameterNames
     * @param classes
     * @param paramName
     * @param clazz
     * @param <T>
     * @return
     */
    private <T> List<T> getParamArray(String[] parameterNames, Object[] classes, String paramName, Class<T> clazz) {
        int index = Arrays.asList(parameterNames).indexOf(paramName);
        List<T> value = (List<T>) classes[index];
        return value;
    }

    /**
     * 格式化内容
     *
     * @param content
     * @return
     */
    private String formatContent(String content) {
        return content == null ? "" : ("了\"" + content + "\"");
    }

    /**
     * 格式化项目任务名称
     *
     * @param itemName
     * @return
     */
    private String formatItemName(String itemName) {
        return itemName == null ? "" : (itemName + "/");
    }
}
